﻿using System;
using System.Collections.Generic;
using System.Linq;
using WI.CandidateManager.Common.Data.Entities;

namespace WI.CandidateManager.Business.Objects
{
    /// <summary>
    /// Logic for working with Tests: creating, editing, deleting etc.
    /// </summary>
    public class TestBO : BOBase
    {
        #region Test logic

        /// <summary>
        /// Getting list of own tests
        /// </summary>
        /// <returns>List of own tests</returns>
        public List<Test> GetTestList()
        {
            try
            {
                if (Session.User.Role == Common.Data.Enums.UserRole.Coach || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    var list = DataContext.TestSet.Where(t => t.Owner.Id == Session.User.Id 
                        && t.State == Common.Data.Enums.State.Active).ToList<Test>();

                    Logger.Info(String.Format("Try to get list of own tests from DB"));
                    return list;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to get list of own tests from DB"), ex);
                return new List<Test>();
            }
        }

        /// <summary>
        /// Getting list of all tests
        /// </summary>
        /// <returns>List of all tests</returns>
        public List<Test> GetAllTestList()
        {
            try
            {
                if (Session.User.Role == Common.Data.Enums.UserRole.Coach || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    var list = DataContext.TestSet.Where(t => t.State == Common.Data.Enums.State.Active).ToList<Test>();
                    Logger.Info(String.Format("Try to get list of all tests from DB"));
                    return list;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to get list of all tests from DB"), ex);
                return new List<Test>();
            }
        }

        /// <summary>
        /// Getting list of Pending tests.
        /// </summary>
        /// <returns>List of Pending tests.</returns>
        public List<Test> GetPendingTestList()
        {
            try
            {
                var coach = DataContext.CoachSet.Single(c => c.Id == Session.User.Id);

                if (coach.Role == Common.Data.Enums.UserRole.Coach)
                {
                    var list = DataContext.TestSet.Where(t => t.ResultBase.Any(r => r.Status == Common.Data.Enums.TaskState.Completed 
                        && r.Candidate.StudyGroup.Any(g => g.Couch.Any(co => co.Id == coach.Id))
                        && r.State == Common.Data.Enums.State.Active) && t.State == Common.Data.Enums.State.Active).ToList<Test>();

                    Logger.Info(String.Format("Try to get list of pending tests from DB"));
                    return list;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to get list of pending tests from DB"), ex);
                return new List<Test>();
            }
        }

        /// <summary>
        /// Get object of Test.
        /// </summary>
        /// <param name="TestId">Test id.</param>
        /// <returns>Object of Test.</returns>
        public Test GetTest(long TestId)
        {
            try
            {
                var test = DataContext.TestSet.Single(t => t.Id == TestId);
                Logger.Info(String.Format("Try to get test by TestId={0} from DB", TestId));
                return test;
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to get test by TestId={0} from DB", TestId), ex);
                return null;
            }
        }

        /// <summary>
        /// Create new test and add it to DataBase.
        /// </summary>
        /// <param name="test">Object of Test from controller.</param>
        /// <returns>True if creating was succesful and false if it wasn't.</returns>
        public bool CreateNewTest(Test test)
        {
            try
            {
                var curUser = DataContext.CoachSet.Where(c => c.Id == Session.User.Id).Single();

                if (curUser.Role == Common.Data.Enums.UserRole.Coach || curUser.Role == Common.Data.Enums.UserRole.Admin)
                {
                    test.Owner = curUser;
                    test.CreateDate = System.DateTime.Now;

                    DataContext.TestSet.Add(test);
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to create new test in DB"));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to create new test in DB"), ex);
                return false;
            }
        }

        /// <summary>
        /// Change object of Test.
        /// </summary>
        /// <param name="TestId">Test id.</param>
        /// <param name="test">Object of test.</param>
        /// <returns>True if editing was succesful and false if it wasn't.</returns>
        public bool ChangeTest(long TestId, Test test)
        {
            try
            {
                var curTest = DataContext.TestSet.Where(t => t.Id == TestId).Single();

                if (curTest.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    curTest.Name = test.Name;
                    curTest.Description = test.Description;
                    curTest.State = test.State;

                    DataContext.Entry(curTest).State = System.Data.EntityState.Modified;
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to change test (TestId={0}) in DB", TestId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to change test (TestId={0}) in DB", TestId), ex);
                return false;
            }
        }

        /// <summary>
        /// Delete object of test.
        /// </summary>
        /// <param name="TestId">Test id.</param>
        /// <returns>True if deleting was succesful and false if it wasn't.</returns>
        public bool DeleteTest(long TestId)
        {
            try
            {
                var test = DataContext.TestSet.Single(t => t.Id == TestId);

                if (test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    if (test.QuestionBase.Count > 0)
                    {
                        foreach (var question in test.QuestionBase.ToList())
                        {
                            DeleteQuestion(question.Id);
                        }
                    }

                    test.State = Common.Data.Enums.State.Deleted;
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to delete test (TestId={0}) from DB", TestId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to delete test (TestId={0}) from DB", TestId), ex);
                return false;
            }
        }

        #endregion

        #region Question logic

        /// <summary>
        /// Get object of Question.
        /// </summary>
        /// <param name="QuestionId">Question id.</param>
        /// <returns>Object of Question.</returns>
        public SingleQuestion GetQuestion(long QuestionId)
        {
            try
            {
                var question = DataContext.SingleQuestionSet.Where(q => q.Id == QuestionId).Single();
                Logger.Info(String.Format("Try to get question (QuestionId={0}) from DB", QuestionId));
                return question;
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to get question (QuestionId={0}) from DB", QuestionId), ex);
                return null;
            }
        }

        /// <summary>
        /// Get new object of Question with link to parent.
        /// </summary>
        /// <param name="TestId">Test id.</param>
        /// <returns>New object of Question.</returns>
        public SingleQuestion GetNewQuestion(long TestId)
        {
            try
            {
                var parentTest = DataContext.TestSet.Where(t => t.Id == TestId).Single();

                if (parentTest.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    SingleQuestion newQuestion = new SingleQuestion();
                    newQuestion.Test = parentTest;
                    Logger.Info(String.Format("Try to get new object of question for test (TestId={0})", TestId));
                    return newQuestion;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to get new object of question for test (TestId={0})", TestId), ex);
                return null;
            }
        }

        /// <summary>
        /// Create new Question and add it to DataBase.
        /// </summary>
        /// <param name="TestId">Test id.</param>
        /// <param name="question">Object of Question from controller.</param>
        /// <returns>True if creating was succesful and false if it wasn't.</returns>
        public bool CreateNewQuestion(long TestId, SingleQuestion question)
        {
            try
            {
                var parentTest = DataContext.TestSet.Where(t => t.Id == TestId).Single();

                if (parentTest.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    question.Test = parentTest;

                    DataContext.SingleQuestionSet.Add(question);
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to create new question for test (TestId={0}) in DB", TestId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to create new question for test (TestId={0}) in DB", TestId), ex);
                return false;
            }
        }

        /// <summary>
        /// Change object of Question.
        /// </summary>
        /// <param name="QuestionId">Question id.</param>
        /// <param name="question">Object of Question.</param>
        /// <returns>True if editing was succesful and false if it wasn't.</returns>
        public bool ChangeQuestion(long QuestionId, SingleQuestion question)
        {
            try
            {
                var curQuestion = DataContext.SingleQuestionSet.Where(q => q.Id == QuestionId).Single();

                if (curQuestion.Test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    curQuestion.Text = question.Text;

                    DataContext.Entry(curQuestion).State = System.Data.EntityState.Modified;
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to change question (QuestionId={0}) in DB", QuestionId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to change question (QuestionId={0}) in DB", QuestionId), ex);
                return false;
            }
        }

        /// <summary>
        /// Delete object of Question.
        /// </summary>
        /// <param name="QuestionId">Question id.</param>
        /// <returns>True if deleting was succesful and false if it wasn't.</returns>
        public bool DeleteQuestion(long QuestionId)
        {
            try
            {
                var question = DataContext.SingleQuestionSet.Single(q => q.Id == QuestionId);

                if (question.Test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    if (question.AnswerBase.Count > 0)
                    {
                        foreach (var answer in question.AnswerBase.ToList())
                        {
                            DeleteAnswer(answer.Id);
                        }
                    }

                    question.State = Common.Data.Enums.State.Deleted;
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to delete question (QuestionId={0}) from DB", QuestionId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to delete question (QuestionId={0}) from DB", QuestionId), ex);
                return false;
            }
        }

        #endregion

        #region Answer logic


        /// <summary>
        /// Get object of Answer.
        /// </summary>
        /// <param name="AnswerId">Answer id.</param>
        /// <returns>Object of Answer.</returns>
        public AnswerBase GetAnswer(long AnswerId)
        {
            try
            {
                var answer = DataContext.AnswerBaseSet.Where(a => a.Id == AnswerId).Single();
                Logger.Info(String.Format("Try to get answer (AnswerId={0}) from DB", AnswerId));
                return answer;
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to get answer (AnswerId={0}) from DB", AnswerId), ex);
                return null;
            }
        }

        /// <summary>
        /// Get new object of Single Answer with link to parent.
        /// </summary>
        /// <param name="QuestionId">Question id.</param>
        /// <returns>New object of Single Answer.</returns>
        public SingleAnswer GetNewSingleAnswer(long QuestionId)
        {
            try
            {
                var parentQuestion = DataContext.SingleQuestionSet.Where(q => q.Id == QuestionId).Single();

                if (parentQuestion.Test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    SingleAnswer newAnswer = new SingleAnswer();
                    newAnswer.QuestionBase = parentQuestion;

                    Logger.Info(String.Format("Try to get new object of single answer for question (QuestionId={0})", QuestionId));
                    return newAnswer;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to get new object of single answer for question (QuestionId={0})", QuestionId), ex);
                return null;
            }
        }

        /// <summary>
        /// Create new Single Answer and add it to DataBase.
        /// </summary>
        /// <param name="QuestionId">Question id.</param>
        /// <param name="answer">Object of Single Answer from controller.</param>
        /// <returns>True if creating was succesful and false if it wasn't.</returns>
        public bool CreateNewSingleAnswer(long QuestionId, SingleAnswer answer)
        {
            try
            {
                var parentQuestion = DataContext.SingleQuestionSet.Where(q => q.Id == QuestionId).Single();

                if (parentQuestion.Test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    answer.QuestionBase = parentQuestion;

                    DataContext.SingleAnswerSet.Add(answer);
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to create new single answer for question (QuestionId={0}) in DB", QuestionId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to create new single answer for question (QuestionId={0}) in DB", QuestionId), ex);
                return false;
            }
        }

        /// <summary>
        /// Get new object of FreeText Answer with link to parent.
        /// </summary>
        /// <param name="QuestionId">Question id.</param>
        /// <returns>New object of FreeText Answer.</returns>
        public FreeTextAnswer GetNewFreeTextAnswer(long QuestionId)
        {
            try
            {
                var parentQuestion = DataContext.SingleQuestionSet.Where(q => q.Id == QuestionId).Single();

                if (parentQuestion.Test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    FreeTextAnswer newAnswer = new FreeTextAnswer();
                    newAnswer.QuestionBase = parentQuestion;
                    Logger.Info(String.Format("Try to get new object of FreeText answer for question (QuestionId={0})", QuestionId));
                    return newAnswer;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to get new object of FreeText answer for question (QuestionId={0})", QuestionId), ex);
                return null;
            }
        }

        /// <summary>
        /// Create new FreeText Answer and add it to DataBase.
        /// </summary>
        /// <param name="QuestionId">Question id.</param>
        /// <param name="answer">Object of FreeText Answer from controller.</param>
        /// <returns>True if creating was succesful and false if it wasn't.</returns>
        public bool CreateNewFreeTextAnswer(long QuestionId, FreeTextAnswer answer)
        {
            try
            {
                var parentQuestion = DataContext.SingleQuestionSet.Where(q => q.Id == QuestionId).Single();

                if (parentQuestion.Test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    answer.QuestionBase = parentQuestion;

                    DataContext.FreeTextAnswerSet.Add(answer);
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to create new FreeText answer for question (QuestionId={0}) in DB", QuestionId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to create new FreeText answer for question (QuestionId={0}) in DB", QuestionId), ex);
                return false;
            }
        }

        /// <summary>
        /// Change object of Single Answer.
        /// </summary>
        /// <param name="AnswerId">Answer id.</param>
        /// <param name="answer">Object of Single Answer.</param>
        /// <returns>True if editing was succesful and false if it wasn't.</returns>
        public bool ChangeSingleAnswer(long AnswerId, SingleAnswer answer)
        {
            try
            {
                var curAnswer = DataContext.SingleAnswerSet.Where(a => a.Id == AnswerId).Single();

                if (curAnswer.QuestionBase.Test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    curAnswer.Text = answer.Text;
                    curAnswer.IsCorrect = answer.IsCorrect;

                    DataContext.Entry(curAnswer).State = System.Data.EntityState.Modified;
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to change Single answer (AnswerId={0}) in DB", AnswerId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to change Single answer (AnswerId={0}) in DB", AnswerId), ex);
                return false;
            }
        }

        /// <summary>
        /// Change object of FreeText Answer.
        /// </summary>
        /// <param name="AnswerId">Answer id.</param>
        /// <param name="answer">Object of FreeText Answer.</param>
        /// <returns>True if editing was succesful and false if it wasn't.</returns>
        public bool ChangeFreeTextAnswer(long AnswerId, FreeTextAnswer answer)
        {
            try
            {
                var curAnswer = DataContext.FreeTextAnswerSet.Where(a => a.Id == AnswerId).Single();

                if (curAnswer.QuestionBase.Test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    curAnswer.Text = answer.Text;
                    curAnswer.IsCorrect = answer.IsCorrect;

                    DataContext.Entry(curAnswer).State = System.Data.EntityState.Modified;
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to change FreeText answer (AnswerId={0}) in DB", AnswerId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to change FreeText answer (AnswerId={0}) in DB", AnswerId), ex);
                return false;
            }
        }

        /// <summary>
        /// Delete object of Answer.
        /// </summary>
        /// <param name="AnswerId">Answer id.</param>
        /// <returns>True if deleting was succesful and false if it wasn't.</returns>
        public bool DeleteAnswer(long AnswerId)
        {
            try
            {
                var answer = DataContext.AnswerBaseSet.Single(a => a.Id == AnswerId);

                if (answer.QuestionBase.Test.Owner.Id == Session.User.Id || Session.User.Role == Common.Data.Enums.UserRole.Admin)
                {
                    answer.State = Common.Data.Enums.State.Deleted;
                    DataContext.SaveChanges();
                    Logger.Info(String.Format("Try to delete answer (AnswerId={0}) in DB", AnswerId));
                    return true;
                }
                else
                {
                    throw new Exception("Attempt of unauthorized access.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(String.Format("Unable to delete answer (AnswerId={0}) in DB", AnswerId), ex);
                return false;
            }
        }

        #endregion
    }
}
